#include <stdio.h>
#include "Hw3Types.h"
#include "Hw3TypesExtended.h"
#include "GlobalVariables.h"
#include "TestFunctions.h"
#include "SyncAndProtectionModule.h"
#include "FacebookModule.h"
#include "FaceComparisonModule.h"
#include "IspImageAnalysis.h"

//Function Declarations
BOOL FindFacesInRow(TArea *Area, int LineIndex);
BOOL FindFacesInArea(TArea *Area);
//End of Function Declarations


BOOL FoundFaceThread(TBox *FoundFaceBox) 
{
	DWORD WaitResult;
	TFace *AddedFace = NULL;

	// check the semaphore that we can start running
	WaitResult = WaitForSingleObject(hNumOfRunningThreadsSemaphore, INFINITE ); 
	if (WaitResult != WAIT_OBJECT_0)
	{
		PrintErrorAndTerminate(ERROR_WAITING_FOR_SINGLE_OBJECT);
		return FALSE;	
	}
	else // we have running permission
	{
		// add the face to the facebook
		AddedFace = AddToFacebookProtected(FoundFaceBox);
		if (AddedFace == NULL)
		{
			PrintErrorAndTerminate(ERROR_ADDING_FACE_TO_FACEBOOK);
			return FALSE;
		}
		free(FoundFaceBox);
		// compare the added face to the facebook
		if (CompareFaceToFacebook(AddedFace) == TRUE)
		{
			//if a matching face was found, terminate the program
			SetEvent(hTerminateProgram);
		}
		
		// release the semaphore to allow other threads to run
		if (ReleaseSemaphore(hNumOfRunningThreadsSemaphore, 1, NULL) == FALSE)
		{
			PrintErrorAndTerminate(ERROR_RELEASING_SEMAPHORE);
			return FALSE;
		}
		DecreaseOpenThreadsCounter();
	}
	return TRUE;
}

void AreaThread(TArea *Area) 
{
	// varibles declaration
	DWORD WaitResult;
	// calculate the number of lines that need to be loaded before we start searching
	int MinimumLineIndexToStartSearching = Area->VerticalIndex+FaceSize.Height+2; 
	
	// wait for enough information to finish loading
	WaitResult = WaitForSingleObject(hReadEvents[MinimumLineIndexToStartSearching],INFINITE);
	if (IsProgramTerminated())
	{
		DecreaseOpenThreadsCounter();
		return;
	}
	if (WaitResult != WAIT_OBJECT_0)
	{

		PrintErrorAndTerminate(ERROR_WAITING_FOR_SINGLE_OBJECT);
	}
	else if (WaitResult == WAIT_OBJECT_0) // enough lines have finished loading
	{
		// wait for the semaphore to allow us do work
		WaitResult = WaitForSingleObject(hNumOfRunningThreadsSemaphore, INFINITE);	
		if (WaitResult != WAIT_OBJECT_0)
		{
			PrintErrorAndTerminate(ERROR_WAITING_FOR_SINGLE_OBJECT);
		}
		else if (WaitResult == WAIT_OBJECT_0) // we have running permission
		{
			// start searching for faces, etc.
			if (FindFacesInArea(Area) == FALSE) 
			{
				// in case of error
				PrintErrorAndTerminate(GENERAL_ERROR);
			}
			// release the semaphore to allow other threads to do work
			if (ReleaseSemaphore(hNumOfRunningThreadsSemaphore,1,NULL) == FALSE)
			{
				PrintErrorAndTerminate(ERROR_RELEASING_SEMAPHORE);
			}
		}
	}
	DecreaseOpenThreadsCounter();
	return;	
}

//Internal Functions
BOOL FindFacesInRow(TArea *Area, int LineIndex)
{
	//variable declerations
	TBox *FoundFaceBox=NULL;
	BOOL FoundFace=FALSE;
	int BoxLeft; 
	int BoxTop = LineIndex;
	HANDLE hFoundFaceThread;

	#ifdef DEBUG_MODE_VERBOSE
		printf("Started searching faces in line %d of area with horizInd %d and vertInd %d)\n", LineIndex, Area->HorizontalIndex, Area->VerticalIndex);
	#endif
	
	// go over all the boxes in the row and look for faces
	for (BoxLeft = Area->OriginX ; BoxLeft < Area->OriginX+Area->Width ; BoxLeft++) //SEE THAT THIS LOOKS IN LAST PIXEL OF THE AREA AS WELL
	{
		#ifdef DEBUG_MODE_VERBOSE
			printf("Searching in line: %d, column: %d)\n", BoxTop, BoxLeft);
		#endif
		
		// look for a face in the specific box
		FoundFace = BoxContainsAFace(Image, BoxTop, BoxLeft, 
			FaceSize.Height, FaceSize.Width, 
			ImageReaderAndProperties.Height, 
			ImageReaderAndProperties.Width);
		
		// if a face has been found, call the found face thread
		if (FoundFace == TRUE)
		{
			#ifdef DEBUG_MODE_VERBOSE
				printf("Found Face in location: (%d, %d)\n", BoxLeft, BoxTop);
			#endif
			// allocare memory for the box in order to pass it on to the FoundFaceThread
			FoundFaceBox = (TBox*)malloc(sizeof(TBox)); //this is freed inside FoundFaceThread
			if (FoundFaceBox == NULL)
			{
				PrintErrorAndTerminate(ERROR_ALLOCATING_MEMORY);
				return FALSE;
			}

			// put the data in
			FoundFaceBox->BoxSize = FaceSize;
			FoundFaceBox->OriginX = BoxLeft;
			FoundFaceBox->OriginY = BoxTop;

			// create the FoundFaceThread thread to add the face to the facebook and start searching for faces
			IncreaseOpenThreadsCounter();
			hFoundFaceThread = CreateThreadSimple((LPTHREAD_START_ROUTINE)FoundFaceThread, (LPVOID)FoundFaceBox , NULL); 
			if (hFoundFaceThread == NULL)
			{
				DecreaseOpenThreadsCounter();
				PrintErrorAndTerminate(ERROR_CREATING_THREAD);
				return FALSE;
			}	
		}
	}	
	return TRUE;
}

BOOL FindFacesInArea(TArea *Area)
{
	//variable declerations
	int LineIndex;
	DWORD WaitResult;

	// for debug mode
	#ifdef DEBUG_MODE
		printf("Started searching for faces in area with horizontal index: %d and vertical index: %d\n", Area->HorizontalIndex, Area->VerticalIndex);
	#endif

	// itterate over all lines in the area, and find the faces in them
	for (LineIndex=Area->OriginY ; 
			(LineIndex < (Area->OriginY+Area->Height)) 
			&& (LineIndex < ImageReaderAndProperties.Height-FaceSize.Height-1) ; //don't search the last two lines (according to post in moodle)
			LineIndex++)
	{
		// make sure enough information has finished loading and we can start running
		WaitResult = WaitForSingleObject(hReadEvents[LineIndex+FaceSize.Height+1], INFINITE); 
		if (WaitResult == WAIT_OBJECT_0) 
			FindFacesInRow(Area, LineIndex);
		else
		{
			PrintErrorAndTerminate(ERROR_WAITING_FOR_SINGLE_OBJECT);
			return FALSE;
		}
	}
	return TRUE; 
}
//End of Internal Functions

